package com.citi.infocambio.siscomex.client.parser;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;

/**
 * 
 * Default parsing routine is:
 * 1. Remove unwanted string on the raw HTML
 * 2. Crop input HTML
 * 3. Remove double blank lines
 * 4. Remove the comments
 * 5. Remove the HTML escape chars such as &nbsp;
 * 6. If xsl transform is needed:
 * 	6.1 Fix unbalanced inputs
 * 	6.2 Transform
 *
 * @author jr23508
 *
 */
public abstract class BaseHTMLParser {
	
	private static final transient Logger log = Logger.getLogger(BaseHTMLParser.class);
	
	private String html;
	private String xsl;
	private String crop;
	
	protected BaseHTMLParser(String html) { 
		this.html = html;
		this.crop = html;
	}
	
	protected BaseHTMLParser(String html, String xsl) { 
		this.html = html;
		this.xsl = xsl;
		this.crop = html;
	}
	
	
	protected abstract String[] getStringsToRemove();
	protected abstract CropDef[] getCrops();	
	protected abstract String[] getUnbalancedInputs();
	
	
	public String parse() throws ParserException { 
		
		log.debug("Parsing received HTML: " + crop);
		log.debug("Step 1 - Remove unwanted strings ...");
		removeUnwantedStrings();
		
		log.debug("Step 2 - Crop ...");
		crop();
		
		log.debug("Step 3 - Remove double blank lines ...");
		removeDoubleBlankLines();			
		
		log.debug("Step 4 - Remove comments ...");
		removeComments();
		
		log.debug("Step 5 - Remove HTML escape ...");		
		removeHtmlScape();
			
		if (xsl != null) { 
			try {				
				
				log.debug("Step 6.1 - Fix unbalanced inputs ...");
				fixUnbalancedInputs(getUnbalancedInputs());			
				
				log.debug("Step 6.2 - Fix unbalanced <br> ...");
				fixUnbalancedBR();
				
				log.debug("Step 6.3 - Fix unbalanced <br> ...");
				fixMissingBraces();
				
				log.debug("Step 6.4 - Transform ...");
				transform();
				
			} catch (FileNotFoundException e) {
				throw new ParserException(e);
			} catch (TransformerException e) {
				throw new ParserException(e);
			}
		} else { 
			log.debug("No xsl transformation.");
		}
		
		log.debug("Parsing result: " + crop);
		
		return crop;
	}
		
	protected void removeUnwantedStrings() { 
		if (getStringsToRemove() != null) { 
			for (String stringToRemove : getStringsToRemove()) { 
				removeString(stringToRemove);
			}			
			log.debug("Unwanted strings removed.");
			log.trace("Resulting HTML: " + crop);
		} else { 
			log.debug("No unwanted strings to remove.");
		}
	}
	
	protected void crop() {
		if (getCrops() != null) { 
			for (CropDef crop : getCrops()) { 
				crop(crop.getFrom(), crop.getTo());
			}
			log.debug("Crop done. ");
			log.trace("Resulting HTML: " + crop);
		} else { 
			log.debug("Nothing to crop.");
		}
	}
	
	protected void removeDoubleBlankLines() { 
		crop.replaceAll("\n\n", "\n");
		log.debug("Double blank lines removed.");
		log.trace("Resulting HTML: " + crop);
	}
	
	protected void removeComments() {	
		
		String commentStart = "<!--";
		String commentEnd = "-->";
		
		int commentStartIndex;
		int commentEndIndex;
		
		StringBuilder buff = new StringBuilder(crop);
		
		while ((commentStartIndex = buff.indexOf(commentStart)) != -1  && (commentEndIndex = buff.indexOf(commentEnd)) != -1) {
			String comment = buff.substring(commentStartIndex, commentEndIndex + commentEnd.length());
			buff = buff.delete(commentStartIndex, commentEndIndex + commentEnd.length());
			log.debug("Removed comment: " + comment);
		}
		
		crop = buff.toString();
		log.debug("Comments removed.");
		log.trace("Resulting HTML: " + crop);
		
	}
	
	protected void removeHtmlScape() { 
		crop = StringEscapeUtils.unescapeHtml(crop);
		log.debug("HTML escape removed.");
		log.trace("Resulting HTML: " + crop);
	}
	
	protected void fixUnbalancedInputs(String... inputs) { 
		if (inputs != null) {
			for (String input : inputs) { 
				fixUnbalancedInput(input);
			}
			log.debug("Unbalanced inputs fixed.");
			log.trace("Resulting HTML: " + crop);
		} else {
			log.debug("No unbalanced inputs to fix.");
		}
	}
	
	
	
	
	
	
	private void removeString(String stringToRemove) {
		log.debug("Removing string: " + stringToRemove);
		crop = crop.replaceAll(stringToRemove, "");
	}
	
	private void crop(String from, String to) { 
		
		log.debug("Cropping from: " + from + " to: " + to);
		
		int startIndex = crop.indexOf(from);
		crop = crop.substring(startIndex);
		
		int endIndex = crop.indexOf(to) + to.length();
		crop = crop.substring(0, endIndex);
		
	}
	
	private void fixUnbalancedInput(String inputName) {
		
		log.debug("Fixing unbalanced input: " + inputName);
		
		Pattern p = Pattern.compile("\\<input.+\"" + inputName + "\"[^\\>]+");
		Matcher m = p.matcher(getCrop());
		String input = null;
		
		while (m.find()) { 
			input = m.group();
		}		
		
		String text = crop;
		int insertionPoint = text.indexOf(input) + input.length();
		StringBuilder buff = new StringBuilder(text);
		buff.insert(insertionPoint, "/");

		crop = buff.toString();
		
	}
	
	private void fixUnbalancedBR() { 
		
		crop = crop.replaceAll("<br>", "<br/>");
		
		log.debug("Unbalanced <br> fixed.");
		log.trace("Resulting HTML: " + crop);
		
	}
	
	private void fixMissingBraces() { 
		
		crop = crop.replaceAll("type=text", "type=\"text\"");
		log.debug("Fixed missing braces.");
		log.trace("Resulting HTML: " + crop);
	}
	
	private void transform() throws FileNotFoundException, TransformerException {
		
		log.debug("Transforming using XSL: " + xsl);
		
		TransformerFactory factory = TransformerFactory.newInstance();	
		InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(xsl);
		Templates template = factory.newTemplates(new StreamSource(is));
		Transformer transformer = template.newTransformer();
		
		StringWriter stringWriter = new StringWriter();		
		
		Source source = new StreamSource(new StringReader(crop.toString()));
		Result result = new StreamResult(stringWriter);
		
		transformer.transform(source, result);		
		
		stringWriter.flush();
		crop = stringWriter.getBuffer().toString();
		
		log.debug("Transformed.");
		log.trace("Resulting HTML: " + crop);
		
	}	


	
	

	
	class CropDef { 
		String from;
		String to;
		public CropDef(String from, String to) { 
			this.from = from;
			this.to = to;
		}
		protected String getFrom() {
			return from;
		}
		protected String getTo() {
			return to;
		}
	}

	protected String getCrop() {
		return crop;
	}
	protected void setCrop(String crop) { 
		this.crop = crop;
	}

	protected String getHtml() {
		return html;
	}

	protected void setHtml(String html) {
		this.html = html;
	}
	
}
